# coding=utf-8
import logging
import time
from api.OutsideApi.bpm_api import Bpm_Outside_Api
from common.assert_operator import *
from common.db import *
from common.utils import Utils

string_assert = string_assert()  # 字符串校验类
pact = Pact()  # 契约校验类
execute_sql = sql()  # sql集合
utils = Utils()


def jp(obj, expr, result_type='VALUE', debug=0, use_eval=True):
    Logger.info(f'jsonpath表达式：{expr}')
    return jsonpath.jsonpath(obj, expr, result_type='VALUE', debug=0, use_eval=True)


class BusinessProcessBase:

    def approve(self, param, user_login_no_session, session, task_id, business_class):
        """
        通过业务系统进入流程中心进行新建和审批
        Args:
            param: 数据入参：case_online/scene_01_myyscm/01_1_create/test_myspace_02_02_1.yml
            user_login_no_session: 登陆对象，也就是from api import login_api下的Login类。通过user_login_no_session.Login(session)可以使用业务session进入流程的页面类
            session: 业务系统登陆后的session
            task_id: 业务系统任务id
            business_class: 业务系统类的实例化对象，用于调用业务系统类下的各种方法。

        Returns:

        """
        login_with_session = user_login_no_session.Login(session)
        node_id, package_id, proc_id, business_id = self.create_process(param, login_with_session)
        # 进入外部接口类
        outside_api: Bpm_Outside_Api = login_with_session.goto_Bpm_Outside_Api()
        # 通过业务GUID获取流程实例步骤信息
        process_name, todo_info, step_status = self.get_todo_steps_info(outside_api, business_id)
        while (todo_info):  # 如果待办信息不为空列表，则进行循环审批
            for nodes in todo_info:
                step_name = nodes['step_name']
                approve_node_id = nodes['approve_node_id']
                approve_user = nodes['approve_user']
                # 根据用户id，获取用户code和用户姓名
                get_users_resp = outside_api.get_by_ids([approve_user])
                users_code = jp(get_users_resp.json(), f'$.data[*].code')[0]
                users_name = jp(get_users_resp.json(), f'$.data[*].name')[0]
                print(f'【{process_name}】开始审批步骤【{step_name}】，审批人【{users_name}】')
                # sess, approve_url = myscm.login_myscm('retesting', 'retesting', 'Sh123456')
                # 根据用户code，登录业务系统，然后根据task_id审批业务流程
                password = business_class.users[users_code]
                sess, approve_url = business_class.business_approve(users_code, password, task_id)
                approve = user_login_no_session.Login(sess).goto_Approve_Process()
                # approve = user_login_no_session.Login().goto_Approve_Process(users_code,'sI1tbN1k1ihj6dfevg1U+Q==')
                # 根据流程id，节点ID，包id打开流程实例
                # process_instance_response = approve.open_process_instance(proc_id=proc_id, node_id=approve_node_id, package_id=package_id)
                process_instance_response = approve.open_process_instance(url=approve_url)
                if nodes['is_step_2'] is True and param['second_action']:
                    operate = param['second_action']
                    self.approve_action(approve, process_instance_response, process_name, users_code, operate)
                else:
                    self.approve_action(approve, process_instance_response, process_name, users_code)
                # 审批后，查询流程步骤详情
                process_name, todo_info, step_status = self.get_todo_steps_info(outside_api, business_id)

    def get_todo_steps_info(self, outside_api, business_guid):
        step_info = outside_api.get_process_instance_steps_view({"businessGuid": business_guid})
        string_assert.eq(step_info.status_code, 200)
        step_view_json = step_info.json()
        # 获取流程名称
        process_name = jp(step_view_json, '$..ProcessInstance.name')[0]
        # 获取所有步骤的状态
        step_status: list = jp(step_view_json, '$..ActualPath[*].status')
        Logger.info(f'步骤状态：{step_status}')
        step_status_flag = 0
        # status=1待办，2在办，3已办，0未激活。如果没有状态1且没有状态2(可能状态未及时刷新)，且最后一个状态不等于3(说明未全部审批)，且不等于-1和-2(终止作废)，则进行循环查询进行等待，则跳出循环
        while ((1 not in step_status and 2 not in step_status) and step_status[-1] != 3 and step_status[-1] != -1 and
               step_status[-1] != -2):
            time.sleep(1)
            step_status_flag += 1
            if step_status_flag > 10:  # 如果查询10次还没有查询到步骤状态为2的，判断审批失败
                assert False, f'超时10s后，{process_name}审批后状态错误，没有待审批(status=2)的步骤'
            step_info = outside_api.get_process_instance_steps_view({"businessGuid": business_guid})
            step_status: list = jp(step_info.json(), '$..ActualPath[*].status')
        # 将状态数组去重，如果全是3，说明已经归档
        set_status = list(set(step_status))
        if set_status == [3]:
            assert True, f'【{process_name}】流程已归档'
        elif -1 in set_status:
            assert True, f'【{process_name}】流程已终止'
        elif -2 in set_status:
            assert True, f'【{process_name}】流程已作废'
        elif -4 in set_status:
            assert True, f'【{process_name}】流程已关闭'
        else:
            assert True, f'【{process_name}】流程进行中。。。'
        # 通过流程实例步骤信息，获取当前流程步骤名称和待办节点责任人id
        todo_step_path = '$..ActualPath[?((@.status==1 || @.status==2) && @.step_type!=8)]'  # 步骤状态为1或2，且步骤类型不为8(分流合流)
        step_ids = jp(step_view_json, f'{todo_step_path}.step_id')  # 获取流程待办步骤ID,['aaaa','bbbbb']
        todo_info = []
        if step_ids:  # 如果待办步骤查询为False，则跳出循环
            for step_id in step_ids:
                steps = {}
                # 根据待办步骤id，获取待办步骤节点id和节点责任人id
                step_name = jp(step_view_json,
                               f'$..ActualPath[?(@.step_id=="{step_id}")].name')[0]
                approve_node_id = jp(step_view_json,
                                     f'$..ActualPath[?(@.step_id=="{step_id}")].nodes[?(@.node_status==1 || @.node_status==2)].node_id')[
                    0]
                approve_user = jp(step_view_json,
                                  f'$..ActualPath[?(@.step_id=="{step_id}")].nodes[?(@.node_status==1 || @.node_status==2)].auditor_id')[
                    0]
                # 过滤掉分流合流步骤后
                step_2 = jp(step_view_json, f'$..ActualPath[?(@.step_type!=8)]')[1]
                # 判断待办节点是否为第2步第1个节点
                step_2_node_1 = jp(step_view_json, f'$..ActualPath[?(@.step_type!=8)]')[1]['nodes'][0]
                # 判断当前待办步骤ID与排除分流后的第二个索引步骤ID是否一致，来判断当前步骤是否为第二个步骤。然后判断是否为第二步骤第一节点，然后进行第二步骤操作。
                if step_2['step_id'] == step_id and step_2_node_1['node_id'] == approve_node_id:
                    is_step_2 = True
                else:
                    is_step_2 = False
                # 构建成字典，用于后续审批时使用
                steps.update({'step_name': step_name,
                              'approve_node_id': approve_node_id,
                              'approve_user': approve_user,
                              'is_step_2': is_step_2})
                # 构建每个节点审批所需参数[{step_name:xxx,node_id:xxx,auditor_id:xxx},{step_name:xxx1,node_id:xxx1,auditor_id:xxx1}]
                todo_info.append(steps)
        return process_name, todo_info, step_status

    def get_business_id(self, outside_api, node_id, package_id, proc_id):
        # 通过流程实例ID获取流程业务GUID
        open_process_params = f'node_id={node_id}&proc_id={proc_id}&package_id={package_id}'
        open_process = outside_api.get_process_info_by_approve(open_process_params)
        string_assert.eq(open_process.status_code, 200)
        # 获取业务GUID
        business_guid = jp(open_process.json(), '$.data.params.business_id')[0]
        return business_guid

    def create_process(self, param, login_with_session):
        """
        新建流程
        """
        user = param['user']
        full_url = param['full_url']
        # 通过传入的session进入创建流程类
        create_sess = login_with_session.goto_Create_Process()
        # response = user_login_no_session.goto_Create_Process(user).initiate_submit(param, opinion='发起流程')
        # 通过外部完整url，打开新建流程页面,获取业务id
        open_process_resp = create_sess.process_create(full_url=full_url)
        # 发起流程
        init_process_resp = create_sess.initiate_submit(param, opinion='测试', open_process_resp=open_process_resp)
        # response = user_login_no_session.goto_Create_Process(user).process_create(full_url=full_url)
        string_assert.eq(init_process_resp.status_code, 200)
        # 获取发起后的关键信息
        proc_id = jp(init_process_resp.json(), '$.data.proc_id')[0]
        package_id = jp(init_process_resp.json(), '$.data.package_id')[0]
        node_id = jp(init_process_resp.json(), '$.data.node_id')[0]
        business_id = jp(open_process_resp.json(), '$.data.params.business_id')[0]
        return node_id, package_id, proc_id, business_id

    def approve_action(self, process, process_instance_response, process_name, user, operate=None):
        """
        通过传入operate，开始对应审批：'Pass','Vote', 'RollBack', 'Consult', 'Assigned', 'Terminate', 'Cancel', 'AddSign'
        """
        if operate is None:
            action = process.approval_rights()  # 根据返回的权限，组装匹配的json入参进行审批提交
        else:
            action = operate
        # 得到按钮后，action就能决定进行什么操作
        approve_params = {'process_name': process_name, 'user': user, 'action': action}
        print(approve_params)
        # 如果有分支，有步骤责任人选择，需要调整脚本，不能默认选择
        approve_resp = process.approve_submit(approve_params, process_instance_response)
        string_assert.eq(approve_resp.status_code, 200)
        # 定义契约格式，然后进行契约契约校验
        if action[0] in ["驳回", "作废"]:  # 驳回和终止响应契约格式一样
            contract_format = pact.roll_back_pact_format()
        elif action[0] in ["加签", "终止", "协商", "交办"]:
            contract_format = pact.addsign_pact_format()
        else:
            contract_format = pact.approve_pact_format()
        pact.pact_verify(contract_format, approve_resp.json())
